<?php

/*
The MIT License

Copyright (c) 2008 T.J. Leahy Jr.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/



/**
* MySQL Interface
*/
Class MySql
{

	private $db_user = "DB_USER";
	private $db_pass = "DB_PASS";
	private $db_database = "DB_DATABASE";
	private $db_host = "DB_HOST";

        /**
	* @var boolean		When true, will print error messages when they occur with all the info
	*/
	public $debugging = false;


	/**
	* @var resource		Mysql resource link identifier
	*/
	protected $db;


	/**
	* @var string		Most recent SQL
	*/
	public $sql;


	/**
	* @var string		Default table being used for generate queries
	*/
	public $table;

	/**
	* @var resource		The most recent result resource
	*/
	public $result;

	/**
	* @var array		The most recent result from select() statements
	*/
	public $last_result;

	/**
	* @var int		Number of rows in result set
	*/
	private $num_rows;

	/**
	* @var int		Number of rows affected
	*/
	private $affected_rows;

	/**
	* @var int		Last insert id from insert() statements
	*/
	public $last_insert_id;

	/**
	* @var string		Most recent error message
	*/
	public $curr_error = false;

	/**
	* @var array		All error messages
	*/
	public $all_errors = array();





	/**
	* Create MySql object
	*
	* @param mixed $table String sets this->table, bool false prevents connection w/default settings
	* @return MySql
	*/
	public function __construct($table = true) {
		if (is_string($table)) {
			$this->table = $table;
		}
		if ($table) {
			$this->connect();
		}
	}

	/**
	* If debugging is enabled, print all error messages to the queue
	*/
	public function __destruct() {
		if ($this->debugging) {
                        foreach ($this->all_errors as $error) {
				$this->print_error($error['msg'], $error['sql']);
			}
		}
	}


	/**
	* Allow mysql method overloading
	*
	* @param string $func 	Function name where mysql_$func is a valid function
	* @param mixed $args 	Argument for $func(), defaults to $this->result
	*
	* @example echo $db->num_rows();
	* @deprecated
	*/
	public function __call($func, $args = false) {
		$arg = ($args) ? $args[0] : $this->result;

			if (function_exists("mysql_$func")) {
				$func = "mysql_$func";
				return $func($arg);
			}
		return false;
	}



	/**
	* Connect to the database (boolean safe)
	*
	* @param mixed $host 	Use host other than $this->db_host;
	* @param mixed $user	Use user other than $this->db_user;
	* @param mixed $pass	Use pass other than $this->db_pass;
	* @param mixed $db		Use database otehr than $this->db_database;
	*/
	public function connect($host = false, $user = false, $pass = false, $db = false) {
		$host = (is_string($host)) ? $host : $this->db_host;
		$user = (is_string($user)) ? $user : $this->db_user;
		$pass = (is_string($pass)) ? $pass : $this->db_pass;
		$db = (is_string($db)) ? $db : $this->db_database;
		$this->db = @mysql_connect($host, $user, $pass);
		if ($db && (@mysql_select_db($db, $this->db))) {
			return true;
		} else {
			$this->add_error("Error connecting to database -- ". mysql_error());
			return false;
		}
	}




	/**
	* Perform a database query
	*
	* @param mixed $sql Pass SQL to override $this->sql;
	* @return mixed
	*
	* @link http://www.woyano.com/jv/ezsql
	*/
	public function query($sql = false) {
		$this->sql = ($sql) ? $sql : $this->sql;
		$this->result = @mysql_query($this->sql, $this->db);

		if ($str = @mysql_error($this->db)) {
			$this->add_error("Error performing SQL query -- $str", $this->sql);
			return false;
		}

		if (preg_match("/^(insert|delete|update|replace)\s+/i", $this->sql)) {
			$this->affected_rows = @mysql_affected_rows($this->db);

			if (preg_match("/^(insert|replace)\s+/i",$this->sql)) {
				$this->last_insert_id = mysql_insert_id($this->db);
			}
		} else {
			$this->get_results();
		}

		return $this->result;
	}





	/**
	* Extracts query results into an array
	*
	*/
	protected function get_results() {
		$i=0;
		$results = array();
		while ( $row = mysql_fetch_assoc($this->result) ) {
			$results[$i] = $row;
			$i++;
		}
		$this->num_rows = $i;
		$this->last_result = $results;
		return $this->last_result;
	}

	/**
	* Return a single row from the results
	*
	* @param int $y	Row offset
	* @return array
	*/
	public function get_row($y = 0) {
		return ($this->last_result[$y]) ? $this->last_result[$y] : null ;
	}


	/**
	* Get a column of results from a select() result
	*
	* @param int $x collumn offset
	* @return array
	*/
	public function get_col($x = 0) {
		$arr = array();
		for($i = 0; $i<count($this->last_result); $i++) {
			$arr[$i] = $this->get_var($x, $i);
		}
		return $arr;
	}


	/**
	* Get a single value from the results
	*
	* @param int $x  	Row
	* @param mixed $y	Column
	* @return mixed
	*/
	public function get_var($x = 0, $y = 0) {
		$row = $this->get_row($y);

		 if (isset($row[$x])) {
			return $row[$x];
		} else {
			$row = @array_values($row);
			return (isset($row[$x])) ? $row[$x] : null;
		}
	}


	/**
	* Get the amount of rows returned from a select statement
	*/
	public function num_rows() {
		return (is_int($this->num_rows)) ? $this->num_rows  : mysql_numrows($this->result);
	}



	/**
	* Select from database, populate $this->last_result
	*
	* @param string $rows 	Table rows to select from
	* @param mixed $extra	Extra query parameters
	* @param mixed $from	Table(s) to use other than $this->table;
	* @return resource
	*/
	public function select($rows = "*", $extra = "", $from = false) {
		$table = ($from) ? $from : $this->table ;
		$this->sql = "SELECT $rows FROM $table  $extra";
		if ($this->query()) {
			return $this->result;
		}
		return false;
	}






	/**
	* Insert values into the database
	*
	* @param string $cols	String of column names to insert into
	* @param array $vals	Array of values
	* @param bool $safe		If $vals doesn't need sanitized
	*/
	public function insert($cols, $vals, $safe = false) {
		$vals = ($safe) ? $vals : $this->sanitize($vals);
		$this->sql = "INSERT INTO {$this->table} ( $cols ) VALUES ( " .implode(", ", $vals). " )";

		return ($this->query());
	}




	/**
	* Update row(s) in the database
	*
	* @param mixed $where 		Dynamic where clause
	* @param string $cols		String of column names
	* @param array $vals		Array of values
	* @param mixed $safe		If $vals doesn't need sanitized
	* @return bool
	*/
	public function update($where, $cols, $vals, $safe = false) {
		$vals = ($safe) ? $vals : $this->sanitize($vals);
		$vals = array_values($vals); //remove array keys to allow the loop
		$cols = explode(',', $cols);
		//check to make sure the # of columns match the number of values
		if (count($cols) != count($vals)) {
		    $this->add_error("Error updating {$this->table}.  Invalid Arguments (column / value count mismatch)");
		    return false;
		}

		$this->sql = "UPDATE {$this->table} SET ";
		$q = array();
		for ($i = 0 ; $i < count($cols); $i++) {
		    $q[] = sprintf(" %s = %s ", $cols[$i], $vals[$i]);
		}

		$this->sql .= implode(", ", $q);
		$this->sql .= $this->getWhereClause($where);

		return $this->query();
	}


	/**
	* Remove row(s) from the database
	*
	* @param mixed $where	Dynamic where clause
	* @param mixed $table	Table to delete from
	* @return bool
	*/
	public function remove($where, $table = false) {
		$table = (is_string($table)) ? $table : $this->table;
		return $this->query( "DELETE FROM $table ".$this->getWhereClause($where) );
	}




	/**
	* Sanitize variable for querying
	*
	* @param mixed $var 		The variable to sanitize
	* @param bool $quotes 		Whether or not to add '' around strings
	* @param mixed $numstrings	Whether or not to treat numbers as strings
	* @return $var
	*/
	public function sanitize($var, $quotes = true, $numstrings = false) {
		if (is_array($var)) {   //run each array item through this function (by reference)
		    foreach ($var as &$val) {
			$val = $this->sanitize($val, $quotes, $numstrings);
		    }
		}
		else if (is_null($var)) {   //convert null variables to SQL NULL
		    $var = "NULL";
		}
		else if (is_bool($var)) {   //convert boolean variables to binary boolean
		    $var = ($var) ? 1 : 0;
		}
		else if ($numstrings && is_string($var)) {
		    $var = mysql_real_escape_string($var);
		    if ($quotes) {
			$var = "'". $var ."'";
		    }
		}
		else if (!is_numeric($var)) { //clean strings
		    $var = mysql_real_escape_string($var);
		    if ($quotes) {
			$var = "'". $var ."'";
		    }
		}
		return $var;
	}


	/**
	* Generate a dynamic mysql WHERE clause.
	*
	* @param mixed $where
	* @return string
	*/
	protected function getWhereClause($where) {
		$where = (is_numeric($where)) ? " WHERE id = ".intval($where) : ((preg_match("/^\s*WHERE/i", $where) < 1) ? " WHERE $where" : " $where" );
		return $where;
	}


	/**
	* Add error messages for debugging
	*
	* @param string $msg	The error message
	* @param mixed $sql	any associated SQL
	*/
	public function add_error($msg, $sql = false) {
		$this->curr_error = array('msg'=>$msg, 'sql'=>$sql);
		$this->all_errors[] = $this->curr_error;
	}

       	/**
	* Output an error message
	*
	* @param string $msg 	The message string
	* @param string $sql	Any relative SQL
	*/
	private function print_error($msg, $sql = false) {
		print "<p><b>MySql Error:</b> $msg";
		if ($sql) print "<br /><b>SQL:</b> $sql";
		print "</p><hr />";
	}

}
?>